
#pragma once

#ifndef _fmbTL
#define _fmbTL

#include "fmbPrimitive.h"

namespace fm {
  namespace base {

/// <summary>
///   Typelist template.
///   If type T (Head) is NullType, then behaivior is not defines.
/// </summary>
template<typename T, typename U>
struct TL {
  typedef T Head;
  typedef U Tail;
};

// Macros which makes construction of various TL easy
#define TL_0 NullType
#define TL_1(T1) TL<T1, NullType>
#define TL_2(T1, T2) TL<T1, TL_1(T2)>
#define TL_3(T1, T2, T3) TL<T1, TL_2(T2, T3)>
#define TL_4(T1, T2, T3, T4) TL<T1, TL_3(T2, T3, T4)>
#define TL_5(T1, T2, T3, T4, T5) \
  TL<T1, TL_4(T2, T3, T4, T5)>
#define TL_6(T1, T2, T3, T4, T5, T6) \
  TL<T1, TL_5(T2, T3, T4, T5, T6)>
#define TL_7(T1, T2, T3, T4, T5, T6, T7) \
  TL<T1, TL_6(T2, T3, T4, T5, T6, T7)>
#define TL_8(T1, T2, T3, T4, T5, T6, T7, T8) \
  TL<T1, TL_7(T2, T3, T4, T5, T6, T7, T8)>
#define TL_9(T1, T2, T3, T4, T5, T6, T7, T8, T9) \
  TL<T1, TL_8(T2, T3, T4, T5, T6, T7, T8, T9)>
#define TL_10(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) \
  TL<T1, TL_9(T2, T3, T4, T5, T6, T7, T8, T9, T10)>
#define TL_11(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) \
  TL<T1, TL_10(T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)>
#define TL_12(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) \
  TL<T1, TL_11(T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)>
#define TL_13(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) \
  TL<T1, TL_12(T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13)>
#define TL_14(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) \
  TL<T1, TL_13(T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14)>
#define TL_15(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) \
  TL<T1, TL_14(T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15)>

/// <summary>
///   Template for the TL length calculation.
/// </summary>
template <class TL> struct TLLength;

template <> struct TLLength<NullType> {
  enum { value = 0 };
};

// works, but there is incostention in behaivior wit non strict element access
//template <typename U> struct TLLength<TL<NullType, U> > {
//  enum { value = 0 };
//};

template <typename T, typename U> struct TLLength<TL<T, U> > {
  enum { value = 1 + TLLength<U>::value };
};

/// <summary>
///   Template for index access to elements.
/// </summary>
template <class TL, unsigned int index> struct TLAt;

template <typename T, typename U> struct TLAt<TL<T, U>, 0> {
  typedef T Type;
};

template <typename T, typename U, unsigned int index> struct TLAt<TL<T, U>, index> {
  typedef typename TLAt<U, index - 1>::Type Type;
};

// works, but there is incostention in behaivior wit non strict element access
//template <typename T, unsigned int index> struct TLAt<TL<NullType, T>, index> {
//};

/// <summary>
///   Template for index access to elements with non strict.
/// </summary>

template <class TL, unsigned int index, typename Default> struct TLAtNonStrict;

template <unsigned int index, typename Default>
struct TLAtNonStrict<TL_0, index, Default> {
  typedef Default Type;
};

template <typename T, typename U, typename Default>
struct TLAtNonStrict<TL<T, U>, 0, Default> {
  typedef T Type;
};

template <typename T, typename U, unsigned int index, typename Default>
struct TLAtNonStrict<TL<T, U>, index, Default> {
  typedef typename TLAtNonStrict<U, index - 1, Default>::Type Type;
};

  // end of namespace fm::base
  }
// end of namespace fm
}

#endif